﻿using System.Reflection;
using System.Collections;
using Microsoft.Xna.Framework;
namespace System.Lua.Serialization
{
    public partial class LuaSerializer
    {
        public readonly static string TableName = "Result";
        public readonly static string TypeTableAttribute = "TypeAttribute";
        LuaInterface.Lua lua;
        Type type;

        public LuaSerializer(Type type)
        {
            this.type = type;
            lua = new LuaInterface.Lua();
        }

        public void Serialize(LuaWriter luaWriter, object o)
        {
            if (luaWriter == null)
                throw new ArgumentNullException("luaWriter");
            luaWriter.WriteStartTableField(TableName, false);
            this.SerializeImpl(luaWriter, o);
        }

        private void SerializeImpl(LuaWriter luaWriter, object o)
        {
            // If object is null, serialize as literal (to 'nil')
            if (o == null)
                this.SerializeLiteral(luaWriter, o);
            else
            {
                // Get the type of the object
                Type type = o.GetType();

                if (type.SerializeAsDictionary())
                    this.SerializeDictionary(luaWriter, (IDictionary)o);
                else if (type.SerializeAsEnumerable())
                    this.SerializeEnumerable(luaWriter, (IEnumerable)o);
                else if (type.SerializeAsClassOrStruct())
                    this.SerializeClassOrStruct(luaWriter, o);
                else if (type.SerializeAsLiteral())
                    this.SerializeLiteral(luaWriter, o);
            }
        }

        private void SerializeEnumerable(LuaWriter luaWriter, IEnumerable collection)
        {
            luaWriter.WriteStartTable();

            foreach (var item in collection)
            {
                luaWriter.WriteStartTableField();
                this.SerializeImpl(luaWriter, item);
                luaWriter.WriteEndTableField();
            }

            luaWriter.WriteEndTable();
        }

        private void SerializeDictionary(LuaWriter luaWriter, IDictionary dictionary)
        {
            luaWriter.WriteStartTable();

            foreach (DictionaryEntry item in dictionary)
            {
                luaWriter.WriteStartTableField();
                this.SerializeImpl(luaWriter, item);
                luaWriter.WriteEndTableField();
            }
            luaWriter.WriteEndTable();
        }

        private void SerializeClassOrStruct(LuaWriter luaWriter, object o)
        {
            Type type = o.GetType();

            luaWriter.WriteStartTable();
            WriteType(luaWriter, o.GetType());
            foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (propertyInfo.CanRead && propertyInfo.CanWrite)
                {
                    luaWriter.WriteStartTableField(propertyInfo.Name, false);
                    this.SerializeImpl(luaWriter, propertyInfo.GetValue(o, null));
                    luaWriter.WriteEndTableField();
                }
            }

            foreach (var fieldInfo in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
            {
                luaWriter.WriteStartTableField(fieldInfo.Name, false);
                this.SerializeImpl(luaWriter, fieldInfo.GetValue(o));
                luaWriter.WriteEndTableField();
            }

            luaWriter.WriteEndTable();
        }

        private void WriteType(LuaWriter luaWriter, Type type)
        {
            luaWriter.WriteStartTableField(TypeTableAttribute, false);
            this.SerializeImpl(luaWriter, type.AssemblyQualifiedName);
            luaWriter.WriteEndTableField();
        }

        private void SerializeLiteral(LuaWriter luaWriter, object value)
        {
            luaWriter.WriteLiteralExpression(value);
        }


    }
}
